﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;

using AForge.Video;
using AForge.Video.DirectShow;


using Graphene.Storing;

namespace Graphene.Processing
{
    public sealed class VideoSource : ProcessorSource, MyNamed
    {
        public VideoSource(Model model, string deviceId, string myId, string myName)
        {            
            this.model = model;
            this.myId = myId;
            this.myName = myName;
            this.deviceId = deviceId;

            this.videoCaptureDevice = new VideoCaptureDevice(deviceId);
            this.imageLock = new Object();
            this.bitmap = null;
            this.byteImage = null;
            this.nResultChanges = 0;

            positionInWorld = new PointReal3D(0.0f, 0.0f, 0.0f);
            angleInWorld = new PointReal2D(0.0f, (float)(-Math.PI / 2));
            fOV = new PointReal2D((float)(40 * Math.PI / 180), (float)(30 * Math.PI / 180));

            this.Stop();

            this.videoCaptureDevice.DesiredFrameSize = new Size(640, 480);
            this.videoCaptureDevice.DesiredFrameRate = 30;
            this.videoCaptureDevice.NewFrame += new NewFrameEventHandler(VideoCaptureDevice_NewFrame);               
        }

        public void Start()
        {
            if (!this.videoCaptureDevice.IsRunning)
            {
                this.videoCaptureDevice.Start();
            }
        }

        public bool IsRunning()
        {
            return this.videoCaptureDevice.IsRunning;
        }

        public void Stop()
        {
            if (this.videoCaptureDevice.IsRunning)
            {
                this.videoCaptureDevice.Stop();
            }
        }

        public void Close()
        {
            if (this.videoCaptureDevice.IsRunning)
            {
                this.videoCaptureDevice.SignalToStop();
            }
            this.bitmap = null;
            this.byteImage = null;
            this.videoCaptureDevice = null;
        }

        /**
         * Gets called whenever the video device captures a new frame.
         * This is where the magic happens.
         */
        private void VideoCaptureDevice_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            lock (this.imageLock)
            {
                this.bitmap = (Bitmap)eventArgs.Frame.Clone();
                this.bitmapWidth = this.bitmap.Width;
                this.bitmapHeight = this.bitmap.Height;
                this.byteImage = AbstractProcessor.BitmapToByteImage_Unsafe(this.bitmap);
            }

            this.nResultChanges++;

            if (this.resultChangedCall != null)
            {
                this.resultChangedCall(this, null);
            }
        }

        /**
         * Resets the nSourceChanges to 0. Used for measuring the source changes per seconds.
         */
        public void ResetNResultChanges()
        {
            this.nResultChanges = 0;
        }

        public override bool Equals(Object obj)
        {
            if (obj == null)
            {
                return false;
            }

            VideoSource vs = obj as VideoSource;
            if ((System.Object)vs == null)
            {
                return false;
            }

            return this.myId.Equals(vs.MyId);
        }

        public override int GetHashCode()
        {
            return this.myName.GetHashCode();
        }

        public override string ToString()
        {
            return this.myName;
        }

        /**
         * Whenever the source changes, the methods (delegates) added by this method will
         * be called.
         */
        public void AddResultChangedHandler(Graphene.Processing.ResultChangedHandler handler)
        {            
            this.resultChangedCall += handler;
        }

        /**
         * Whenever the source changes, the methods (delegates) removed by this method will
         * not be called.
         */
        public void RemoveResultChangedHandler(Graphene.Processing.ResultChangedHandler handler)
        {
            this.resultChangedCall -= handler;
        }


        /* Getters/setters: */

        public string MyId
        {
            get { return myId; }
        }

        public string MyName
        {
            get { return myName; }
        }

        public string DeviceId
        {
            get { return deviceId; }
        }
        
        public Bitmap Bitmap
        {
            get
            {
                lock (this.imageLock)
                {
                    return bitmap;
                }
            }
        }

        public int BitmapWidth
        {
            get { return this.bitmapWidth; }
        }

        public int BitmapHeight
        {
            get { return this.bitmapHeight; }
        }

        public Object ResultData
        {
            get { return byteImage; }
        }

        public int NResultChanges
        {
            get { return nResultChanges; }
        }

        public PointReal3D PositionInWorld
        {
            get { return positionInWorld; }
            set { positionInWorld = value; }
        }

        public PointReal2D AngleInWorld
        {
            get { return angleInWorld; }
            set { angleInWorld = value; }
        }

        public PointReal2D FOV
        {
            get { return fOV; }
        }


        /* Attributes: */

        private Model model;

        /**
         * Identification of the VideoSource.
         * Something like:
         */
        private string myId;

        /**
         * Readable name of the VideoSoruce. Does not neet to be unique (but appreciated by the user).
         */
        private string myName;

        /* Id of the physical device. */
        private string deviceId;

        private VideoCaptureDevice videoCaptureDevice;

        public Object imageLock;

        /**
         * Captured bitmap image by the webcam, camera or other video device/source.
         */
        private Bitmap bitmap;

        private int bitmapWidth;

        private int bitmapHeight;

        /**
         * The source for the Processor.
         * An image coded into a byte array that is usually processed by an ImageProcessor.
         * However the name is 'source', this is the main output of the class(/interface).
         */
        private byte[] byteImage;

        /**
         * Number of changes of the source since the last reset.
         */
        private int nResultChanges;

        /** Properties of the camera devices in the real world.
         * Where are they mounted? What is their lens's field of view? */
        private PointReal3D positionInWorld;
        private PointReal2D angleInWorld;
        private PointReal2D fOV;

        private event Graphene.Processing.ResultChangedHandler resultChangedCall;
    }
}
